
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Text;

using Swaf;
using Swaf.BizRule;
using Swaf.BizObj;
using Swaf.Container;

using MediaCache;

namespace MediaCacheAdmin
{
    /// <summary>Provides the media cache creation, deletion and update access bizrules that
    /// are specifically for managing media caches and not the media items contained within them.
    /// Each media cache is defined as a catalog entry in the ImageCache catalog directory which is
    /// also made available through app.appdata["images"].
    /// </summary>
	public class MediaCacheBusinessRules : Syrinx.BaseBizRules
    {
        /// <summary>
        /// Will create a new media cache based on the id and name provided.  This includes creating the catalog
        /// entry and the media directories that newly added and scaled media will be placed into.  The media directory
        /// names are based on the media cache id.
        /// </summary>
        /// <param name="newId">The unique id to use for the media cache.  If the id is not unique this method will
        /// throw an exception.</param>
        /// <param name="dummy">A dummy parameter to avoid the rulemanager bug associated with using strings for the 2nd param.</param>
        /// <param name="newName">The name to use for the new image cache.</param>
        /// <param name="basedOnId">Can be null, but if provided this bizrule will use the media cache that has this
        /// id as the template for the new image cache.  If null, the new media cache will be based on the template
        /// stored in the app.appData["defaultImageCacheSetting"] which is specified in the MediaCacheAdminFeature.xml file.</param>
        /// <returns>true if the newly created image cache is the new default, false if not.</returns>
        public bool addMediaCache(IApplication app, IRuleContext context, string newId, bool dummy, string newName, string basedOnId)
        {
            try
            {
                IBizObj imageCache = null;
                IResults rc = null;

                if (basedOnId != null)
                {
                    rc = app.ruleMgr.execute("getImageCacheDetails", basedOnId);
                    if (rc.ExceptionInfo != null)
                        throw rc.ExceptionInfo;

                    if (rc.CallResults != null)
                    {
                        imageCache = rc.CallResults as IBizObj;
                        imageCache.put("isDefault", false);
                    }
                }
                else
                {
                    imageCache = app.appData.get("defaultImageCacheSetting") as IBizObj;
                    imageCache = imageCache.Clone() as IBizObj;
                    imageCache.put("isDefault", true);
                }

                StringBuilder buff = new StringBuilder(100);
                buff.Append("$BaseCacheStore$").Append(Path.DirectorySeparatorChar).Append(newId);

                imageCache.put("name", newName);
                imageCache.put("id", newId);
                imageCache.put("baseCacheLocation", buff.ToString());
                buff.Append(Path.DirectorySeparatorChar).Append("Original");
                imageCache.put("origionalImagesLocation", buff.ToString());

                validateDirectoryLocation(imageCache);
                rc = app.ruleMgr.execute("saveImageCacheDetails", imageCache);
                if (rc.ExceptionInfo != null)
                    throw rc.ExceptionInfo;

                return (bool)imageCache.get("isDefault");
            }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Error executing addMediaCache.", e);
            }
        }

        /// <summary>
        /// Will update the catalog entry for the media cache provided as well as change the directory name
        /// used for the image store if the media cache id has changed.  The directory name used for the media
        /// cache is the same as the media cache id.
        /// </summary>
        /// <param name="oldId">The previous id of the media cache, which must always be provided even if the
        /// id has not changed.  This is needed to properly find the catalog entry and directories for the media
        /// cache if the id did change.</param>
        /// <param name="cacheInfo">The image caches with updated information in it.  This information is what
        /// will be stored in the catalog entry for the image cache.</param>
        /// <returns></returns>
        public string updateMediaCache(IApplication app, IRuleContext context, string oldId, IBizObj cacheInfo)
        {
            if (cacheInfo == null)
                throw new MediaCacheAdminException("The cacheInfo param cannot be null.");

            try
            {
                string newId = cacheInfo.get("id").ToString();
                if (newId != oldId)
                {
                    cacheInfo.put("id", newId);
                    string dir = (App.resMgr.resolve(cacheInfo.get("baseCacheLocation").ToString()));
                    if (dir.StartsWith(app.resMgr.getMacro("BaseCacheStore")))
                    {
                        try
                        {
                            DirectoryInfo cacheDir = new DirectoryInfo(dir);
                            string newCacheDir = cacheDir.Parent.FullName + Path.DirectorySeparatorChar + newId;
                            string basePath = "$BaseCacheStore$" + Path.DirectorySeparatorChar + newId;
                            cacheDir.MoveTo(newCacheDir);
                            cacheInfo.put("origionalImagesLocation", Path.Combine(basePath, "Original"));
                            cacheInfo.put("baseCacheLocation", basePath);
                        }
                        catch (Exception){ throw; }
                    }

                    //Need to delete the file for the old id.
                    deleteImageCacheFile(oldId);
                }
                IResults rc = app.ruleMgr.execute(context, "saveImageCacheDetails", cacheInfo);
                if (rc.ExceptionInfo != null)
                    throw rc.ExceptionInfo;
                return oldId;
            }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Unable to update image cache library.", e);
            }
        }

        /// <summary>
        /// This bizrule provides the capability to remove a given mediaCache file defination
        /// including the database images and image cache directories related to it
        /// </summary>
        /// <param name="name">The name of the media cache to delete</param>
        /// <param name="deleteCached">If true, this method will delete the sized images created
        /// from the origional.  If false, these images will be left in the file system.</param>
        /// <param name="deleteOriginal">If true, this method will delete the origional images
        /// put into the media cache only if those images are located within the BaseCacheStore
        /// directory. If false, this method will leave the images.</param>
        public void deleteMediaCache(IApplication app, IRuleContext context, string name, bool deleteCached, bool deleteOriginal)
        {
            try
            {
                if (name != null)
                {
                    IResults rc = null;
                    deleteImageCacheFile(name);
                    rc = app.ruleMgr.execute("getImageCacheDetails", name);
                    if (rc.ExceptionInfo != null)
                        throw rc.ExceptionInfo;
                    IBizObj cache = rc.CallResults as IBizObj;
                    Debug.Assert(cache != null);
                    string dirName = app.resMgr.resolve(cache.get("baseCacheLocation").ToString());
                    DirectoryInfo rootDir = null;
                    if (deleteCached)
                    {
                        rootDir = new DirectoryInfo(dirName);
                        foreach (DirectoryInfo di in rootDir.GetDirectories())
                            if (di.Name.ToLower() != "original")
                                di.Delete(true);
                    }
                    if (deleteOriginal && dirName.StartsWith(app.resMgr.getMacro("BaseCacheStore")))
                        Directory.Delete(Path.Combine(dirName, "Original"), true);
                }
            }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Error executing deleteMediaCache.", e);
            }
        }

        /// <summary>
        /// Given an media cache, this bizrule will look at each scaling and each of its image adjustment
        /// specifications and if it has been changed, delete the temporary scaling images in the media storage
        /// directory.
        /// </summary>
        /// <param name="imageCache">The image cache to validate the scaling updates with.</param>
        public void updateImageCacheScaling(IApplication app, IRuleContext context, IBizObj imageCache)
        {
            if (imageCache == null)
                throw new MediaCacheAdminException("The imageCache param cannot be null.");

            try
            {
                string dir = app.resMgr.resolve(imageCache.get("baseCacheLocation").ToString());
                IBizObjList scalings = imageCache.get("scalings") as IBizObjList;
                foreach (IBizObj scale in scalings)
                {
                    string name = scale.get("name").ToString();
                    dir = Path.Combine(dir, (name == "") ? "_default" : name);
                    IBizObjList adjustments = scale.get("adjustments") as IBizObjList;
                    //clear image cache directory for adjustments that are dirty
                    foreach (IBizObj adj in adjustments)
                        if (adj.isDirty())
                        {
                            dir = dir + Path.DirectorySeparatorChar + adj.get("name").ToString();
                            if (Directory.Exists(dir))
                                Directory.Delete(dir, true);
                        }
                }
                //Write MediaCache File to disk
                IResults rc = app.ruleMgr.execute("saveImageCacheDetails", imageCache);
                if (rc.ExceptionInfo != null)
                    throw rc.ExceptionInfo;
            }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Error executing updateImageCacheScaling", e);
            }
        }

        /// <summary>
        /// This bizrule does the work of creating the "images" catalog entry file based on the MediaCache bizobj
        /// provided.  If there is already an existing catalog entry with a matching id it will be overwritten.
        /// </summary>
        /// <param name="cacheInfo">The MediaCache bizobj to store as a catalog entry.</param>
        public void saveImageCacheDetails(IApplication app, IRuleContext context, IBizObj cacheInfo)
        {
            if (cacheInfo == null)
                throw new MediaCacheAdminException("The parameter cacheInfo cannot be null");

            try
            {
                string fullName = deleteImageCacheFile(cacheInfo.get("id").ToString());
                if (fullName.Length > 0)
                {
                    FileInfo cacheFile = new FileInfo(fullName);
                    FileStream fs = cacheFile.OpenWrite();
                    TextWriter writer = new StreamWriter(fs);
                    writer.Write(cacheInfo.getXml("public setOnly"));
                    writer.Close();
                    fs.Close();
                }
            }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Error executing SaveImageCacheDetails bizRule.", e);
            }
        }

        /// <summary>
        /// Given an array of media cache names, this bizrule will iterate through each of the media caches
        /// and add the new scaling based on the provided name and the scale information provided in the 
        /// app.appData["defaultImageCacheSettings.scalings.[0]"] scaling.
        /// </summary>
        /// <param name="cacheNames">The array of media cache names to add the new scaling to.</param>
        /// <param name="dummy">A dummy param to avoid the rulemanager bug related to using strings as the 2nd param.</param>
        /// <param name="name"></param>
        public void addScalingTo(IApplication app, IRuleContext context, string[] cacheNames, bool dummy, string name)
        {
            if (cacheNames == null)
                throw new MediaCacheAdminException("The parameter cacheNames cannot be null");

            IBizObj scale = app.appData.get("defaultImageCacheSetting.scalings.[0]") as IBizObj;
            Debug.Assert(scale != null);
            scale.put("name", name);
            foreach (string cache in cacheNames)
            {
                IResults rc = app.ruleMgr.execute("getImageCacheDetails", cache);
                if (rc.ExceptionInfo != null)
                    throw new MediaCacheAdminException("Error executing addScalingTo:getImageCacheDetails.");
                IBizObj cacheInfo = rc.CallResults as IBizObj;
                if (cacheInfo != null)
                {
                    IBizObjList scaling = cacheInfo.get("scalings") as IBizObjList;
                    scaling.insertNewMakeCopy(scale);
                    rc = app.ruleMgr.execute("saveImageCacheDetails", cacheInfo);
                    if (rc.ExceptionInfo != null)
                        throw new MediaCacheAdminException("Error executing addScalingName:saveImageCacheDetails.");
                }
            }
        }

        protected void validateDirectoryLocation(IBizObj imageCache)
        {
            Debug.Assert(imageCache != null);
            DirectoryInfo origDir = new DirectoryInfo((App.resMgr.resolve(imageCache.get("origionalImagesLocation").ToString())));
            DirectoryInfo cacheDir = new DirectoryInfo((App.resMgr.resolve(imageCache.get("baseCacheLocation").ToString())));
			if (!origDir.Exists)
				origDir.Create();
			
            if (!cacheDir.Exists)
                cacheDir.Create();
        }
        /// <summary>
        /// Delete a known image cache file from the application image cache directory.
        /// The image cache directory is defined in the $BaseLocation$ macro defination. 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        protected string deleteImageCacheFile(string id)
        {
            try
            {
                string fullName = "";
                XmlCatalogContainerNode catalog = Swaf.Application.currentApp.appData.get("images") as XmlCatalogContainerNode;
                FileSystemInfo finfo = catalog.BaseLocation;
                fullName = Path.Combine(finfo.FullName, id);
                fullName += ".xml";
                FileInfo cacheFile = new FileInfo(fullName);
                if (cacheFile.Exists)
                    File.Delete(fullName);
                return fullName;
            }
            catch (Exception e)
            {
                throw new MediaCacheAdminException("Error executing deleteImageCacheFile.", e);
            }
        }
    }
}